#ifndef SMART
#define SMART

#include <iostream>
#include <memory>
#include <functional>
#include <fstream>

namespace SmartPtr
{
    template <class T>
    class shared_ptr
    {
    public:
        explicit shared_ptr() : _ptr(nullptr), _ref_count(nullptr), _del([](T *p)
                                                                         { delete p; }) {}
        explicit shared_ptr(T *ptr) : _ptr(ptr), _ref_count(new int(1)), _del([](T *p)
                                                                              { delete p; }) {}

        template <class D>
        explicit shared_ptr(T *ptr, D del)
            : _ptr(ptr),
              _ref_count(new int(1)),
              _del(del)
        {
        }

        // 实现移动构造，配合std::move()使用
        shared_ptr(shared_ptr &&other) noexcept : _ptr(other._ptr), _ref_count(other._ref_count), _del(std::move(other._del))
        {
            // 直接转移资源
            other._ptr = nullptr;
            other._ref_count = nullptr;
            other._del = [](T *p)
            { delete p; };
        }

        // 实现移动赋值运算符
        shared_ptr &operator=(shared_ptr &&other)
        {
            // 判断自身
            if (_ref_count != other._ref_count)
            {
                if (--(*this._ref_count) == 0)
                {
                    _del(_ptr);
                    delete _ref_count;
                }
                this->_ptr = other._ptr;
                this->_ref_count = other._ref_count;
                this->_del = other._del;
                other._ptr = other._ref_count = nullptr;
            }
        }

        shared_ptr<T> &operator=(const shared_ptr<T> &sp)
        {
            // 处理自己给自己赋值
            if (_ref_count != sp._ref_count)
            {
                // 资源所有权转移，引用计数--
                // 为什么减去自身的refcount,因为要先释放对旧资源的引用
                if (--(*_ref_count) == 0)
                {
                    _del(_ptr);
                    delete _ref_count;
                }
                this->_ptr = sp._ptr;
                this->_ref_count = sp._ref_count;
                this->_del = sp._del;

                if (_ref_count)
                    ++(*_ref_count);
            }

            return *this;
        }

        shared_ptr(const shared_ptr<T> &sp) : _ptr(sp._ptr), _ref_count(sp._ref_count)
        {
            if (this->_ref_count)
                (*_ref_count)++;
        }

        T *get() const
        {
            return _ptr;
        }

        void reset()
        {
            if (this->_ref_count)
            {
                if (--(*(this->_ref_count)) == 0)
                {
                    _del(_ptr);
                    delete _ref_count;
                }
            }
            _ptr = nullptr;
            _ref_count = nullptr;
        }

        template <class D>
        void reset(T *ptr, D del)
        {
            if (this->_ref_count)
            {
                if (--(*(this->_ref_count)) == 0)
                {
                    _del(_ptr);
                    delete _ref_count;
                }
            }

            this->_ptr = ptr;
            this->_del = del;
            this->_ref_count = new int(1);
        }

        // 防止隐式类型转换
        explicit operator bool()
        {
            return _ptr != nullptr;
        }

        T *operator->() const
        {
            return get();
        }

        T &operator*() const
        {
            return *get();
        }

        long use_count() const
        {
            if (_ref_count == nullptr)
                return 0;
            return *_ref_count;
        }

        ~shared_ptr()
        {
            if (_ref_count)
            {
                if (--(*_ref_count) == 0)
                {
                    // 这里一定注意使用删除器，如果我们传入的删除器的释放资源方式和这里不一样程序就会崩溃
                    _del(_ptr);
                    delete _ref_count;
                }
                _ptr = nullptr;
                _ref_count = nullptr;
            }
        }

    private:
        T *_ptr; // 原始指针
        // 引用计数
        int *_ref_count;
        std::function<void(T *)> _del = [](T *ptr)
        { delete ptr; };
    };

    template <class T>
    class unique_ptr
    {
    };

    template <class T>
    class weak_ptr
    {
    };
}

class TestClass
{
public:
    TestClass(int test)
    {
        _test = test;
    }
    int getVar() { return _test; }
    ~TestClass() {}

private:
    int _test;
};

void TestSmartPointer_shared_ptr()
{
    using SmartPtr::shared_ptr;

    auto del = [](TestClass *p)
    {
        std::cout << "[deleter] called for value=" << p->getVar() << std::endl;
        delete p;
    };

    std::cout << "== create a ==" << std::endl;
    shared_ptr<TestClass> a(new TestClass(10));
    std::cout << "a.get()=" << (a.get() ? "not null" : "null") << ", a.use_count()=" << a.use_count() << std::endl;

    std::cout << "\n== copy b = a ==" << std::endl;
    shared_ptr<TestClass> b = a;
    std::cout << "after copy: a.use_count()=" << a.use_count() << ", b.use_count()=" << b.use_count() << std::endl;

    std::cout << "\n== move c = std::move(a) ==" << std::endl;
    shared_ptr<TestClass> c(std::move(a));
    std::cout << "after move: a.get()=" << (a.get() ? "not null" : "null")
              << ", a.use_count()=" << a.use_count()
              << ", c.get()=" << (c.get() ? "not null" : "null")
              << ", c.use_count()=" << c.use_count() << std::endl;

    std::cout << "\n== reset c with custom deleter ==" << std::endl;
    c.reset(new TestClass(20), del);
    std::cout << "after reset with deleter: c.get()=" << (c.get() ? "not null" : "null")
              << ", c.use_count()=" << c.use_count() << std::endl;

    std::cout << "\n== reset b (no-arg) ==" << std::endl;
    b.reset();
    std::cout << "after reset(): b.get()=" << (b.get() ? "not null" : "null")
              << ", b.use_count()=" << b.use_count() << std::endl;

    std::cout << "\n== scope end, destructors will run ==" << std::endl;
}

void TestFileResource()
{
    using SmartPtr::shared_ptr;
    auto fileDeleter = [](std::fstream *fs)
    {
        if (fs && fs->is_open())
        {
            fs->close();
            std::cout << "File closed by deleter" << std::endl;
        }
        delete fs;
    };

    std::cout << "== Create shared_ptr for file ==" << std::endl;
    std::fstream *file = new std::fstream("test.txt", std::ios::out | std::ios::app);
    shared_ptr<std::fstream> filePtr(file, fileDeleter);
    if (filePtr && filePtr->is_open())
    {
        *filePtr << "Hello from shared_ptr!" << std::endl;
    }
    std::cout << "filePtr use_count: " << filePtr.use_count() << std::endl;

    std::cout << "\n== Copy shared_ptr ==" << std::endl;
    shared_ptr<std::fstream> filePtr2 = filePtr;
    if (filePtr2)
    {
        *filePtr2 << "Copied handle" << std::endl;
    }
    std::cout << "filePtr use_count: " << filePtr.use_count() << ", filePtr2 use_count: " << filePtr2.use_count() << std::endl;

    std::cout << "\n== Reset to empty ==" << std::endl;
    filePtr.reset();
    std::cout << "After reset: filePtr use_count: " << filePtr.use_count() << std::endl;

    std::cout << "\n== Scope end, file will be closed ==" << std::endl;
}

struct ListNode
{
    ListNode() : val(-1) {}

    ~ListNode()
    {
        std::cout << "~ListNode()" << std::endl;
    }

    int val;
    std::weak_ptr<ListNode> prev;
    std::weak_ptr<ListNode> next;
};
void Testfunc()
{
    // using SmartPtr::shared_ptr;
    std::shared_ptr<ListNode> n1(new ListNode());
    std::shared_ptr<ListNode> n2(new ListNode());
    n1->next = n2;
    n2->prev = n1;
    // 这里会报错，信息为不存在从 "SmartPtr::shared_ptr<ListNode>" 到 "int *" 的适当转换函数
}

#endif